vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbacknächstes Kapitel


Woche 3

Tag 20



Was noch bleibt

Allmählich nähert sich dieses Buch seinem Ende, und Sie haben sich mit den meisten Aspekten von Perl bereits vertraut gemacht oder zumindest hineingeschnuppert. Doch mit welchem Thema man sich auch beschäftigt, immer bietet Perl alternative Möglichkeiten und weitere Lösungswege, die ich aus Platzgründen nicht alle beschreiben konnte.

Betrachten Sie deshalb dieses Kapitel als Vertiefung zum ganzen Buch. Wir werden heute eine Reihe von Themen erörtern, die ich bisher nicht angesprochen habe, weil sie entweder zu komplex oder von zu peripherer Bedeutung für die einzelnen Kapitelinhalte waren. Diese Themen umfassen:

Einzeilige Perl-Skripts

Wenn Sie ein Perl-Skript schreiben, wird Ihre Vorgehensweise meistens so aussehen, wie bisher in diesem Buch beschrieben: Sie schreiben das Skript, speichern es in einer Datei und bedienen sich dann des Perl-Interpreters, um das Skript auszuführen. Manchmal jedoch haben Sie es mit einer wirklich einfachen Aufgabe zu tun oder aber mit einer einmaligen (oder zumindest sehr selten anfallenden) Aufgabe. Für Probleme dieser Art wäre es reine Zeitverschwendung, extra den Editor aufzurufen, nur um das eigentliche Skript zu schreiben. Abhilfe schaffen in diesen Fällen die einzeiligen Skripts von Perl, sogenannte Einzeiler.

Einzeiler sind Perl-Skripts, die Sie direkt an der Perl-Befehlszeile eingeben. Sie werden nirgends gespeichert. Haben Sie einen Fehler gemacht, müssen Sie sie neu eingeben.

Um einen Einzeiler in Perl zu erstellen, verwenden Sie die Option -e gefolgt von dem Skript in Anführungszeichen:

%  perl -e 'print "Dies ist ein Einzeiler\n";'
Dies ist ein Einzeiler
%

Unter Windows müssen Sie das ganze Skript in doppelte Anführungszeichen setzen und die doppelten Anführungszeichen im Skript selbst mit einem Backslash kennzeichnen:

C:\> perl -e "print \"Dies ist ein Einzeiler unter Windows\n\";"

Arbeiten Sie mit MacPerl, so haben Sie keine Befehlszeile. Doch keine Panik! Es gibt im Skriptmenü einen passenden Menübefehl, der die Befehlszeile simuliert und in den Sie Ihren Perl-Einzeiler eingeben können. Aber auch hier müssen Sie das Wort perl, die Option -e und das Skript in Anführungszeichen eingeben.

Wenn Ihr Skript mehrere Anweisungen enthält, so setzen Sie sie einfach alle in eine Zeile (in den meisten Unix-Shells können Sie einen Befehl auf mehrere Zeilen verteilen, indem Sie ein Backslash (\) an das Ende der Zeile setzen). Zur Erinnerung: Perl stört sich nicht an Zwischenraumzeichen (Whitespaces). Deshalb könnten Sie theoretisch einen unglaublich komplexen Einzeiler erzeugen, und Perl hätte trotzdem keine Probleme mit dessen Ausführung (vielleicht haben Sie auch schon den altbekannten Spruch vieler Perl-Programmierer gehört: »Ich erledige das alles in einer Zeile!« - was natürlich möglich ist, da man in Perl alles in einer Zeile machen kann, fragt sich nur, wie lang die Zeile dann wird).

Sehen Sie im folgenden einige Beispiele für einzeilige Perl-Skripts.

So drehen Sie alle Zeilen in einer Datei um:

% perl -e 'print reverse <>;' dateiname.txt

So geben Sie alle Zeilen einer Datei mit Zeilennummern aus:

% perl -e '$i=1;while(<>){print "$i: $_";$i++}' dateiname.txt

So entfernen Sie alle führenden Whitespace-Zeichen aus allen Zeilen einer Datei:

% perl -e 'while(<>){s/^\s+//g;print;}' dateiname.txt

So geben Sie eine Datei in Großbuchstaben aus:

% perl -e 'while(<>){print uc  $_;}' dateiname.txt

Da Skripts dieser Art häufig while-Schleifen mit <> und einer Form von print verwenden, gibt es in Perl dafür eine Kurzform. Die Option -p erlaubt Ihnen, den while(<>)-Teil wegzulassen und $_ dennoch Zeile für Zeile auszugeben. Demzufolge könnte man das Beispiel zur Konvertierung in Großbuchstaben auch folgendermaßen schreiben (ob das wirklich besser ist, müssen Sie selbst entscheiden):

% perl -p -e '$_ = uc $_;' test.txt

Diese Zeile entspricht dem folgenden Code:

while (<>) {
$_ = uc $_;
print;
}

Sie können die beiden Befehlszeilenoptionen auch zusammenfassen, müssen aber die p-Option vor die e-Option stellen:

% perl -pe '$_ = uc $_;' test.txt

-p ist nicht die einzige Perl-Option, die Ihnen in Ihren Einzeilern Tipparbeit abnehmen soll. Die Option -n entspricht in etwa der Option -p, erzeugt aber keinen print-Teil (sie liefert Ihnen lediglich die while(<>)-Schleife). Die Option -l in Kombination mit -p oder -n trägt automatisch dafür Sorge, dass das Neue-Zeile-Zeichen am Ende jeder Zeile erst entfernt und dann beim Ausgeben wieder eingefügt wird. (Oder um genau zu sein, -l setzt den Wert der Variablen $\, dem Trennsymbol für die Ausgabedatensätze, auf $/, dem Trennsymbol für die Eingabedatensätze - in der Regel das Neue-Zeile-Zeichen). Alternativ können Sie -l einen oktalen Wert als zusätzliches Argument mitgeben, der das Zeichen repräsentiert, das Sie als Trennsymbol für die Ausgabedatensätze verwenden wollen.

Einzeiler können extrem leistungsfähig sein, wenn sie zusammen mit der Option -i verwendet werden. Angenommen Sie haben einen Roman geschrieben und auf mehrere Dateien verteilt, die alle auf die Extension .txt enden. Jetzt wollen Sie alle Vorkommen des Namens »Stefan« durch den Namen »Fred« ersetzen. Das einzeilige Perl-Skript, das dies leistet, alle Originaldateien ändert und von jeder Datei eine Sicherungsdatei anlegt, sieht wie folgt aus:

% perl -p -i.bak -e 's/Stefan/Fred/g' *.txt

Die Option -i schreibt direkt in Ihre Originaldateien, so dass die neuen Versionen die gleichen Dateinamen tragen wie die Originaldateien. Die alten Versionen dieser Dateien (in denen Ihr Held noch Stefan hieß) werden in Dateien mit der Extension .txt.bak gespeichert. Auf diese Weise haben Sie immer noch die Möglichkeit, auf die alten Dateien zuzugreifen, für den Fall, dass Sie vielleicht nicht Stefan, sondern Albert in Fred umbenennen möchten. Seien Sie vorsichtig mit diesem Perl-Befehl - testen Sie ihn zuerst einmal an einer einzigen Datei, ohne Änderungen vorzunehmen. So können Sie sicherstellen, dass Ihr Einzeiler auch korrekt funktioniert. Sonst kann es Ihnen passieren, dass Sie am Ende eine ganze Reihe von .bak-Dateien restaurieren müssen.

Objektorientierte Programmierung

Eines der größeren Themen, auf das ich in diesem Buch nicht näher eingegangen bin, betrifft die Verwendung von Perl für die objektorientierte Programmierung, kurz OOP (wäre der Titel dieses Buches Perl in 25 1/2, hätten wir es vielleicht geschafft). Glücklicherweise fällt die Einarbeitung in die objektorientierte Programmierung unter Perl nicht allzu schwer, da Perl bekannte Elemente wie Pakete, Subroutinen und Referenzen verwendet, um eine objektorientierte Programmierumgebung zu schaffen. Auf diese Weise können Sie, wenn Sie sich schon etwas mit OOP auskennen, direkt - unter Beachtung bestimmter Regeln - mit der Programmierung beginnen. Wer in der objektorientierten Programmierung noch unerfahren ist, muss sich zuerst etwas Hintergrundwissen aneignen. Irgendwelche neuen größeren Perl-Features brauchen Sie nicht zu lernen, um Ihre Kenntnisse in objektorientierter Programmierung direkt umzusetzen.

Erste Schritte und ein wenig mehr

Wenn Sie mit objektorientierter Programmierung noch nicht vertraut und daran interessiert sind, objektorientiert zu programmieren, sollte Ihr erster Schritt darin bestehen, dass Sie sich die Grundkonzepte anschauen. Objektorientierte Programmierung bedeutet einfach, dass Sie das gleiche Programmierproblem aus einem anderen Blickwinkel betrachten. Alles, was Sie bisher in diesem Buch über Syntax und guten Programmierstil gelernt haben, behält seine Gültigkeit. Der Unterschied liegt darin, wie Ihr ganzes Skript organisiert ist und wie es sich verhält.

Der Grundgedanke, der der objektorientierten Programmierung zugrunde liegt, ist, dass Ihr Skript keine Sammlung von nacheinander ausgeführten Anweisungen und Subroutinen ist, sondern eine Sammlung von Objekten, die in einer vordefinierten Art und Weise miteinander interagieren. Jedes Objekt hat eine definierte Erscheinung oder Status (Variablen) und einen definierten Satz an Verhaltensweisen (Subroutinen, in OOP auch Methoden genannt). Objekte erhalten diese Verhaltens- und Statusschablonen von einer Klassendefinition. Diese Klassendefinition wiederum erbt (verwendet) oftmals die Merkmale von einer oder mehreren anderen Klassen. Wenn Sie ein objektorientiertes Skript erstellen, erzeugen Sie eine oder mehrere eigene Klassen, die Klassen und Objekte von anderen Quellen (in der Regel Module) importieren und verwenden. Wenn Ihr Perl-Skript ausgeführt wird, werden aus den verschiedenen Klassen Laufzeitobjekte erstellt, die untereinander ihre Variablen ändern und ihre jeweiligen Subroutinen aufrufen, um am Ende eine Art von Ergebnis zu liefern.

Hat Sie der obige Absatz zu Tode erschreckt, dann geraten Sie bitte nicht in Panik. In Perl können Sie sich langsam mit OOP anfreunden und bereits die ersten OOP- Konzepte nutzen, ohne gleich alle Konzepte und Techniken kennen zu müssen. Und außerdem gibt es im Netz eine Fülle von OOP-Tutorials, die Ihnen helfen, die Theorie und die verschiedenen Konzepte der objektorientierten Programmierung zu verstehen. Die folgenden Vorschläge sind ganz gute Ausgangspunkte:

Sollten Sie immer noch verzagt sein, ist auch das noch kein Grund, in Panik zu verfallen. Zwar wird OOP von vielen als der Trend der Zukunft betrachtet, doch wenn Sie zufrieden damit sind, mit dem guten alten Perl weiterzuarbeiten, und niemand von Ihnen erwartet, sich in OOP einzuarbeiten, spricht nichts dagegen, sich an das Altbewährte zu halten. Denn schließlich führen viele Wege nach Rom.

Die Grundlagen (für Leser, die bereits über OOP-Kenntnisse verfügen)

Sie wissen also bereits, was ein Objekt ist und wie es sich zu einer Klasse verhält. Und Sie kennen die Begriffe Instanzvariable, Methode, Vererbung, Konstruktor, Destruktor und Kapselung. Dann lassen Sie uns diese Terminologie auf Perl übertragen.

Klassen, Objekte und Objektreferenzen

In Perl ist eine Klasse ein Paket, und der Namensbereich, der vom Paket definiert wird, definiert die Kapselung und den Gültigkeitsbereich für diese Klasse. Variablen, die in diesem Paket definiert werden, sind Klassenvariablen (statische Variablen). Instanzvariablen werden normalerweise dadurch erzeugt, dass man eine Referenz auf einen Hash erzeugt und als Schlüssel den Namen der Instanzvariablen verwendet. Klassen- und Instanzmethoden werden beide als Subroutinen definiert. Der einzige Unterschied zwischen einer Methode und einer regulären Subroutine ist der, dass eine Methode als erstes Argument einen Klassennamen (für Klassenmethoden) oder eine Objektreferenz (für Instanzmethoden) erwartet.

Eine Objektreferenz? Gut aufgepaßt. Das ist das einzig Neue, das Sie, soweit es Perl betrifft, lernen müssen, um in Perl objektorientiert programmieren zu können. In Perl ist ein Objekt eine Referenz auf eine Datenstruktur (in der Regel ein anonymer leerer Hash), die mit einer speziellen Markierung versehen wurde, so dass sie sich wie ein Objekt verhält und weiß, zu welcher Klasse sie gehört. Um etwas als ein Objekt zu markieren, verwenden Sie die vordefinierte bless-Funktion. Diese Funktion liefert eine Referenz auf ein Objekt zurück, die Sie dann wiederum, wie jede andere Referenz auch, einer Skalarvariablen zuweisen können.

Normalerweise verwenden Sie bless in dem Konstruktor Ihrer Klasse. Der Konstruktor ist eine ganz normale Subroutine, die per Konvention new genannt wird. Die bless-Funktion übernimmt zwei Argumente: das, wofür Sie eine Objektreferenz erstellen wollen, und den Namen einer Klasse. Eine einfache Klassendefinition könnte damit wie folgt aussehen:

package MeineKlasse;
sub new {
my $klassenname = shift;
my $selbst = {};
return bless $selbst, $klassenname;
}

In diesem Beispiel wird davon ausgegangen, dass die Klassenmethode new mit einem Argument aufgerufen wird: einem Klassennamen. In new selbst erzeugen wir einen leeren, anonymen Hash, markieren ihn mittels bless mit dem aktuellen Klassennamen und liefern eine Referenz auf den Hash zurück. Beachten Sie, dass new eine Klassenmethode ist und dass Klassenmethoden immer als erstes Argument den Namen der Klasse erhalten.

Sie können bless auch mit nur einem Argument verwenden (dem Element, das mit bless markiert werden soll), und Perl wird den Namen der aktuellen Klasse automatisch als zweites Argument verwenden. Aufgrund der Art und Weise wie Perl jedoch mit vererbten Methoden umgeht, kann die Verwendung von nur einem Argument zu falschen Ergebnissen führen, wenn eine andere Klasse Ihren Konstruktor erbt. Allgemein möchte ich Ihnen nahelegen, sich daran zu gewöhnen, bless mit zwei Argumenten zu verwenden, und den Klassennamen der Argumentliste der Methode zu entnehmen.

Um ein Objekt zu erzeugen und zu verwenden (aus dieser Klasse oder aus irgendeiner anderen Klasse), rufen Sie die Methode new zusammen mit einem Paket- (Klassen-) namen auf. Sie können dies in der gleichen Datei erledigen, in der auch Ihre Klassendefinition steht, solange Sie vorher in package main wechseln:

package main;
$obj = new MeineKlasse;

Die Skalarvariable $obj enthält dann eine Referenz auf ein Objekt, das durch die Klasse MeineKlasse definiert ist. Alternativ können Sie auch die Pfeilsyntax (mit ->) verwenden, um den new-Konstruktor aufzurufen:

$obj = MeineKlasse->new();

Beide Möglichkeiten, new aufzurufen, führen zum gleichen Ergebnis. Wenn Ihr new- Konstruktor jedoch neben dem Klassennamen noch weitere Argumente benötigt, ist es oft leichter, das letztere Format anzuwenden:

$obj = MeineKlasse->new(12, 240, 15);

Um die Methoden aufzurufen, die in Ihrem neuen Objekt definiert sind, müssen Sie die Objektreferenz dereferenzieren. Dafür eignet sich die ->-Syntax besonders gut:

$obj->eineSubroutine('foo','bar');

Alternativ gibt es für Methoden eine Syntax, die stärker an die normale Funktionsaufruf-Syntax angelehnt ist. Dabei muss das erste Argument zu der Methode der Klassenname oder eine Objektreferenz sein:

eineSubroutine $obj, 'foo', 'bar';

Da das erste Argument hier eine Objektreferenz ist, wird Perl die Referenz für Sie dereferenzieren und die richtige Methode aufrufen.

Sie können eine Methode auch wie eine Funktion in einem Paket aufrufen ((Myclass::eineSubroutine(...)). Diese Vorgehensweise empfiehlt sich aber nur, wenn Sie genau wissen, was Sie machen. Damit untergraben Sie nämlich die OOP- Eigenschaften der Klasse und verlieren die Fähigkeit, an eine Methodendefinition zu gelangen, die Ihnen über Vererbung zur Verfügung steht. Im nächsten Abschnitt »Instanzvariablen« erzähle ich Ihnen noch mehr zum Definieren und Aufrufen von Referenzen.

Instanzvariablen

Wenn Sie wie oben beschrieben Objekte erzeugen, verwenden Sie einen anonymen Hash als das, was Sie mit bless als Objekt markieren. Warum gerade einen Hash? Weil ein Hash zum Speichern und Zugreifen auf Instanzvariablen genutzt werden kann und Sie jedesmal eine neue Version dieser Variablen erhalten. Auf diese Weise werden alle internen Objektstatusinformationen in Perl-Objekten gespeichert.

In manchen OOP-Sprachen wird zwischen Instanzvariablen und Klassenvariablen unterschieden (letztere werden manchmal auch als statische Daten bezeichnet). Perl kennt an sich keine Klassenvariablen. Sie können zwar jederzeit globale Paketvariablen erstellen, die die Funktion von Klassenvariablen haben, und auf diese dann über die normale Paketsyntax ($MeineKlasse::meineKlassenVar) zugreifen, allerdings werden diese Variablen nicht vererbt und können von jedem Benutzer Ihrer Klasse beliebig geändert werden. Versuchen Sie, die Verwendung von Klassenvariablen zu umgehen und statt dessen Instanzvariablen zu verwenden.

Im allgemeinen werden die Instanzvariablen einer Klasse in dem Konstruktor der Klasse definiert und initialisiert. Häufig werden Sie dabei Argumente an new übergeben, die die Anfangswerte dieser Klasse bilden sollen:

#!/usr/bin/perl -w

package Rectangle;
sub new {
my ($classname, $w, $h) = @_;
my $self = {};
$self->{Width} = $w;
$self->{Height} = $h;
return bless $self, $classname
}
sub area {
my $self = shift;
return $self->{Width} * $self->{Height};
}

package main;
$sq = new Rectangle (12, 20);
print "Fläche: ", $sq->area(), "\n";

In diesem Beispiel haben wir einen Klassenkonstruktor verwendet, der zwei zusätzliche Argumente übernimmt - eine Breiten- und eine Höhenangabe. Auf der Grundlage dieser Argumente erzeugt der Konstruktor ein Rectangle-Objekt (Rechteck) und speichert die Werte in dem Hash des Objekts unter den Schlüsseln Width und Height. Des weiteren haben wir eine Methode namens area erzeugt, die die aktuellen Werte dieser Instanvariablen multipliziert und das Ergebnis zurückliefert.

Im allgemeinen sieht die objektorientierte Programmierung mit Instanzvariablen in Perl so aus, dass Sie zum Schreiben und Lesen dieser Variablen Methoden definieren und verwenden, anstatt die Werte der Variablen durch direkte Zuweisung zu ändern. Dies ist insbesondere bei der Vererbung von Vorteil und sorgt dafür, dass Ihre Klasse oder Ihr Objekt eine »reinere« objektorientierte Schnittstelle erhält. Sie können aber auch mit der normalen Dereferenzierungssyntax auf die Instanzvariablen zugreifen:

# kein objektorientierter Variablenzugriff
print "Width: $sq->{Width}\n";
print "Height: $sq->{Height}\n";

Vererbung

Wie jede ordentliche objektorientierte Programmiersprache erlaubt auch Perl die Vererbung von Klassen, die es Klassen erlaubt, die Definitionen anderer Klassen zu verwenden und zu erweitern. Wenn die Klasse B das Verhalten von Klasse A erbt, wird Klasse A als Basis- oder Superklasse und Klasse B als abgeleitete oder Subklasse bezeichnet.

Um anzuzeigen, dass eine Klasse von einer anderen Klasse erbt, verwenden Sie das spezielle Array @ISA. Das Array @ISA legt fest, in welchen Klassen nach Methodendefinitionen gesucht wird, wenn die Definition einer aufgerufenen Methode nicht in der aktuellen Klasse existiert. Wenn Sie zum Beispiel eine Superklasse Katzen hätten, würde die Subklasse Tiger wie folgt aussehen:

package Tiger;
@ISA = qw( Katzen );
...

Wenn eine Klasse nur von einer Superklasse erbt, ist der Aufruf von qw nicht unbedingt erforderlich, erleichtert aber das Hinzufügen weiterer Superklassen, falls man sich später für eine Mehrfachvererbung entscheidet:

package Hauskatze;
@ISA = qw( Katze Haustier );
...

Wenn für eine Methode, die über ein bestimmtes Objekt aufgerufen wird, in der aktuellen Klassendefinition keine Definition gefunden wird, geht Perl das @ISA-Array durch und sucht in jeder der aufgelisteten Superklassen nach der betreffenden Methodendefinition. Gibt es zu einer Superklasse wieder weitere Superklassen werden diese ebenfalls durchsucht, bevor die nächste Superklasse im @ISA-Array an die Reihe kommt. Nehmen wir an, die Methode fressen wird für ein Objekt der Klasse Hauskatze aufgerufen. Zuerst wird in Hauskatze selbst nach einer Definition gesucht, dann in Katze und dann in allen Superklassen von Katze (falls vorhanden) und in deren Superklassen, bevor die Suche nach der Definition in Haustier fortgeführt wird. Die Definition, die zuerst gefunden wird, wird auch verwendet.

Perl vererbt nur Methoden. Um Instanzvariablen zu »vererben«, können Sie geerbte Konstruktormethoden verwenden, um einen einzigen Hash von Instanzvariablen einzurichten, der die Instanzvariablen sämtlicher Superklassen aufnimmt - plus denen, die für die aktuelle Klasse definiert wurden. (Ein Beispiel hierfür finden Sie in der Manpage perlobj.)

Methoden definieren

Das Definieren von Methoden kann in drei allgemeine Kategorien unterteilt werden:

Methoden, die von der Vererbung keinen Gebrauch machen, werden als gewöhnliche Subroutinen in der Klassen-(Paket-)Definition aufgesetzt. Die einzige Besonderheit ist das erste Argument zu dieser Subroutine, das festlegt, ob die Methode eine Klassen- oder eine Instanzmethode ist.

Im Falle von Instanzmethoden ist das erste Argument eine Objektreferenz. Normalerweise wird bei Methoden dieser Art zuerst einmal die Referenz aus der Argumentliste extrahiert und in einer Skalarvariablen gespeichert ($self ist ein sehr geläufiger Variablenname, er kann aber auch beliebig anders lauten):

sub meineMethode {
my $self = shift;
...
}

Bei Klassenmethoden ist das erste Argument einfach der Name der Klasse - nichts Besonderes, nur ein String. Auch hier werden Sie für gewöhnlich das Argument extrahieren und es in einer Skalarvariablen speichern.

Wie sieht es jetzt aber mit Methoden aus, die je nach Argument entweder eine Klassen- oder eine Instanzmethode sein können. Für solche Methoden brauchen Sie einen Weg, wie Sie im Rumpf der Methode feststellen können, ob es sich bei dem ersten Argument um ein Objekt oder eine Klasse handelt. Sehr hilfreich ist in diesem Zusammenhang die Funktion ref:

sub klasseOderInstanz {
my $arg = shift;
if (ref($arg)) {
print "Argument ist ein Objekt\n";
} else {
print "Argument ist ein Klassenname\n";
}
}

Beachten Sie, dass die ref-Funktion, wenn sie mit einer Objektreferenz als Argument aufgerufen wird, den Namen der Klasse zurückliefert, von der das Objekt eine Instanz darstellt.

Wenn Sie eine Methode aufrufen, die in der aktuellen Klasse definiert ist, wird diese Methode ausgeführt - auch wenn es weiter oben in der Vererbungskette eine gleichnamige Methode gibt. Dies ist der Weg, wie Sie Methoden definieren, die bestehende Methoden überschreiben - einfach definieren und fertig.

Wenn Sie das Verhalten einer Methode einer übergeordneten Klasse ergänzen wollen, anstatt es komplett zu überschreiben, verwenden Sie das Paket SUPER, das Perl anweist, in den in @ISA aufgeführten Klassen nach einer Methodendefinition zu suchen:

sub berechnen {
my $self = shift;
my $sum = $self->SUPER::berechnen(); # zuerst die Superklassen

foreach (@_) {
$sum += $_;
}
return $sum;
}

SUPER wird häufig in geerbten Konstruktoren (new-Methoden) verwendet. Mit SUPER können Sie einen Konstruktor erzeugen, der die Vererbungskette hinaufläuft, um sicherzustellen, dass das aktuelle Objekt über alle Instanzvariablen und Initialisierungen verfügt, die es benötigt.

Ich habe Ihnen bereits gezeigt, wie Sie Konstruktormethoden mit bless erzeugen. Sie können aber auch Destruktormethoden erzeugen, die ausgeführt werden, wenn alle Referenzen auf das Objekt gelöscht sind und das Objekt nur noch darauf wartet, von der Speicherbereinigung entfernt zu werden. Sie erzeugen eine Destruktormethode, indem Sie eine Subroutine namens DESTROY in Ihrer Klasse definieren:

sub DESTROY {
print "Objekt löschen\n";
...
}

Selbstladende Methoden

In Verbindung mit den Methodenaufrufen gibt es in Perl eine nette Besonderheit, die ich Ihnen nicht vorenthalten möchte: die sogenannten selbstladenden Methoden. Selbstladende Methoden stellen eine Art letzte Zuflucht dar, wenn Sie für ein Objekt eine Methode aufrufen, für die Perl keine passende Definition finden kann. In diesem Fall versucht Perl, eine Methode namens AUTOLOAD aufzurufen. Die Paketvariable $AUTOLOAD enthält den Namen der Methode, die aufgerufen wurde (einschließlich des ursprünglichen Klassennamens, für den sie aufgerufen wurde). Sie können diese Informationen dann nutzen, um festzulegen, wie ansonsten unbekannte Methodenaufrufe verarbeitet werden sollen. Die Manpage perlobj enthält ein hervorragendes Beispiel dafür, wie man mit Hilfe einer selbstladenden Methode Zugriffsmethoden für Instanzvariablen simulieren kann, ohne dass man tatsächlich für jede Variable eine eigene Methode definieren müßte. Im folgenden finden Sie ein einfaches Beispiel für eine selbstladende Methode, die diese Art von Verhalten zeigt:

package Hauskatze;
@ISA = qw( Katze, Haustier );

sub new {
my $classname = shift;
return bless {}, $classname;
}

sub AUTOLOAD {
my ($self,$arg) = @_;
my $name = $AUTOLOAD;

my $iv =~ s/.*://; # Paket-Teil aus Namen entfernen
if ($arg) { # Wert setzen
$self->{$iv} = $arg;
return $arg;
} else { # kein Argument, Rückgabewert zurückliefern
return $self->{$iv};
}
}

package main;
my $cat = new Hauskatze;
$cat->color("Grau");
print "Meine Katze ist $cat->color()\n";

In diesem Beispiel hat die Klasse Hauskatze keine Methode namens color (und wir gehen davon aus, dass die übergeordneten Klassen ebenfalls keine Methode dieses Namens haben). Wenn die Methode color aufgerufen wird, ruft Perl daher AUTOLOAD auf. In der Definition von AUTOLOAD erhalten wir den Namen der aufgerufenen Methode über die Variable $AUTOLOAD, entfernen den Paketnamen zu Beginn und verwenden den übriggebliebenen Methodennamen dann als Namen der Instanzvariablen. Wurde die Methode mit einem Argument aufgerufen, weisen wir diesen der Instanzvariablen zu. Andernfalls geben wir einfach den aktuellen Wert aus (es obliegt dem Aufrufer, undefinierte Instanzvariablen zu handhaben). Sie können diese AUTOLOAD-Methode genau so einfach auch für alle anderen Instanzvariablen verwenden - name, alter, temperament, lieblingsessen und so weiter.

Technisch gesehen, stellen AUTOLOAD-Methoden nicht unbedingt die letzte Zuflucht dar. Zusätzlich zu AUTOLOAD gibt es noch die Klasse UNIVERSAL. UNIVERSAL ist eine Art globale Superklasse. In ihr können Sie Ihre Zufluchtsmethoden definieren.

Ein Beispiel: Objektorientierte Module in der Praxis

In Perl ist OOP kein Dogma, sondern eine optionale Möglichkeit. Sie können OOP nur sporadisch einsetzen oder die Sache gründlich angehen und OOP konsequent überall verwenden. Das hängt ganz davon ab, was am leichtesten ist und wie fanatisch Sie den Grundprinzipien der objektorientierten Programmierung anhängen.

In vielen Fällen nutzt man die Vorzüge der objektorientierten Programmierung am einfachsten dadurch, dass man die verschiedenen objektorientierten CPAN-Module in die eigenen Skripts einbindet - ohne dass man dazu notwendigerweise die eigenen Skripts als einen Satz von Objekten strukturieren müßte. Erinnern wir uns noch einmal an die CGI-Skripts und das Modul CGI.pm von Tag 16, »Perl für CGI-Skripts«. Dieses Modul ist so geschrieben, dass seine Subroutinen sowohl als einfache Subroutinen als auch als objektorientierte Methoden verwendet werden können. Betrachten wir eine der Übungen, die wir am Ende der Lektion gemacht haben - Übung 1, ein CGI-Skript, das nur die ihm übergebenen Schlüssel und Werte ausgibt - und setzen wir das CGI-Modul diesmal objektorientiert ein.

Das Originalskript sehen Sie noch einmal in Listing 20.1

Listing 20.1: paare1.pl

 1: #!/usr/bin/perl -w
2: use strict;
3: use CGI qw(:standard);
4:
5: my @keys = param();
6:
7: print header;
8: print start_html('Hallo!');
9: print "<H1>Schlüssel/Wert-Paare</H1>\n";
10: print "<UL>\n";
11:
12: foreach my $name (@keys) {
13: print "<LI>$name = ", param($name), "\n";
14: }
15: print "</UL>\n";
16:
17: print end_html;

Wir verwenden in diesem Skript vier Subroutinen aus dem CGI-Moduls: param (Zeilen 5 und 13), die uns die gesamte Liste der verfügbaren Schlüssel und deren Werte liefert, header (Zeile7), um einen CGI-Header auszugeben, start_html (Zeile 8), um den oberen Teil einer HTML-Datei auszugeben, und end_html (Zeile 17), um den unteren Teil einer HTML-Datei auszugeben. Im obigen Beispiel haben wir diese Subroutinen als reguläre Subroutinen verwendet.

Das CGI-Modul kann aber auch als eine objektorientierte Klasse verwendet werden. Erzeugen Sie eine Instanz dieser Klasse, und Sie können die Subroutinen verwenden, als ob es sich dabei um Methoden handeln würde (was sie ja auch eigentlich sind). Alles was hierzu nötig ist, ist eine zusätzliche Codezeile und eine etwas abgeänderte Syntax für die Subroutinen (siehe Listing 20.2).

Listing 20.2: Die objektorientierte Version von paare1.pl

 1: #!/usr/bin/perl -w
2: use strict;
3: use CGI qw(:standard);
4:
5: my $obj = CGI->new();
6: my @keys = $obj->param();
7:
8: print $obj->header();
9: print $obj->start_html('Hallo!');
10: print "<H1>Schlüssel/Wert-Paare</H1>\n";
11: print "<UL>\n";
12:
13: foreach my $name (@keys) {
14: print "<LI>$name = ", $obj->param($name), "\n";
15: }
16: print "</UL>\n";
17:
18: print $obj->end_html();

Sehen Sie die Unterschiede? Eigentlich sind es nur zwei:

Und das Endergebnis? Es gibt keinen Unterschied zu der Nicht-OOP-Version. Das CGI-Modul ist so geschrieben, dass es gleichermaßen gut als normale Sammlung von Subroutinen oder als objektorientierte Klasse verwendet werden kann.

Beachten Sie, dass es sich bei diesem Beispiel nicht um ein »reines« OOP-Programm handelt. Wir haben hier keine eigenen Klassen erzeugt. Ein »wahres« OOP-Skript würde den Großteil des Codes in einer eigenen Klasse unterbringen und dort das CGI- Modul nutzen. Im Hauptteil würde kaum mehr als die Instantiierung der Klasse und die Aufrufe von ein oder zwei Methoden stehen, um das Skript in Gang zu setzen. Das ist das Gute an OOP in Perl. Sie müssen nur so viel objektorientierte Programmierung einsetzen wie nötig. Im Gegensatz zu anderen strengeren OOP-Sprachen, müssen Sie Code, der für OOP ungeeignet erscheint, nicht unbedingt in Objekte pressen.

Formate

In Anbetracht der Tatsache, dass Perl ein Akronym für Practical Extraction and Report Language (Praktische Extraktions- und Reportsprache) ist, mag es Sie überraschen, dass bisher in diesem Buch zwar viel über das Extrahieren geschrieben wurde, aber kaum etwas über das Protokollieren. Für letzteres gibt es die Formate: zur Ausgabe von formatierten textbasierten Protokollen über Informationen, die Sie vorher gelesen, verarbeitet oder anderweitig malträtiert haben.

Warum wird dieses Thema erst jetzt gegen Ende des Buchs angesprochen? Ein großer Teil der Ausgabe von Perl-Skripts erfolgt heutzutage - als Ergebnis von CGI-Skripts - im HTML-Format und nicht mehr im Nur-Text-Format. Wenn Sie also davon ausgehen, dass Sie Perl hauptsächlich für CGI einsetzen werden, ist HTML für die Ausgabe besser geeignet als reiner Text. Wenn Sie aber vornehmlich mit einfacher Textausgabe arbeiten, sollten Sie sich unbedingt mit den Formaten befassen, vor allem auch unter dem Aspekt, dass print-Anweisungen ziemlich umständlich sind, um eine ordentliche und übersichtliche Präsentation Ihrer Ausgabe sicherzustellen.

Die Idee hinter den Formaten ist die, dass Sie mit Hilfe der format-Funktion eine spezielle Schablone (Template) deklarieren, die festlegt, wie Ihre Ausgabe auszusehen hat, und dann mit der write-Funktion und einem Datei-Handle diese Schablone auf die Daten anwenden. Als Ergebnis werden die Platzhalter in der Schablone durch einen Satz von Werten aus der gegebenen Datenmenge ersetzt. Die Schablone legt die Position einer jeden Datenspalte sowie ihre Breite und Ausrichtung fest. Zusätzlich steht Ihnen eine Reihe von speziellen Perl-Variablen für die Größe einer Seite (in Zeilen), den Header (Kopfbereich), der oben auf jeder Seite erscheinen soll, und die aktuelle Seitenzahl zur Verfügung. Insgesamt können Sie so für jede beliebige Art von Daten Protokolle mit Ganzseiten-Textformatierung erzeugen.

Schablonen werden mit Hilfe der Funktion format deklariert. Schablonennamen verhalten sich wie Subroutinen- oder andere Variablennamen, sie unterliegen den gleichen Regeln zur Namensgebung und geraten nicht mit den Namen anderen Arten von Variablen in Namenskonflikt. Eine format-Deklaration enthält einen Namen, eine Reihe von Bildzeilen, die festlegen, wie das Format aussehen soll, und einen Satz an Variablen, die die Daten festlegen, die in die Schablone passen sollen. Das Format endet mit einem einzelnen Punkt in einer eigenen Zeile. Im folgenden sehen Sie ein Beispiel für ein einfaches Format, das eine Reihe von Firmen mit Börsenticker- Symbolen, Höchstpreisen, Niedrigstpreisen und Abschlußpreisen ausgibt:

format STDOUT =
@<<<<<<<<<<<<<<<<<<@||||@|||||@|||||@|||||
$name, $sym,$high,$low, $current
.

Bildzeilen enthalten Symbole für einfache, einzeilige Spalten (@) oder einfache, gefüllte Textspalten (^). Jede Zeile kann auf vielfältige Weise mit verschiedenen Symbolen (<, |, >, #) ausgerichtet werden (links, zentriert, rechts, numerisch). Die Variablen geben an, welche Daten an den einzelnen Positionen im Format stehen sollen, wenn die Zeile ausgegeben wird. Wenn Sie use strict verwenden, müssen Sie diese Variablen vorher mit my deklarieren, bevor Sie sie in dem Format verwenden können.

Um die formatierten Daten an ein Datei-Handle auszugeben, verwenden Sie die write-Funktion:

write STDOUT;

Beachten Sie, dass der Name des Formats mit dem Namen des Datei-Handles, an das die Ausgabe erfolgt, übereinstimmen muss (obwohl Sie dies mit dem Modul FileHandle ändern können). In unserem Fall habe ich das Standard-Datei-Handle STDOUT verwendet.

Betrachten wir noch ein Beispiel. Unsere Daten sind eine einfache Aufgabenliste, wobei der Datensatz aus den folgenden Feldern besteht: Zeitpunkt der Fertigstellung, Priorität der Aufgabe (1 bis 5), ob erledigt oder nicht (0 oder 1) und einer Textbeschreibung der Aufgabe (ein String von ungefähr 40 Zeichen oder weniger). Diese Daten sind in einer Datei gespeichert und werden in ein Array von Hashes (@data) eingelesen (siehe gestrige Lektion). Ihr Skript soll unter anderem dazu in der Lage sein, die Aufgabenliste nach Prioritäten sortiert in einem ansprechenden Format auszugeben. Eine entsprechende Ausgabe könnte zum Beispiel wie folgt aussehen:

Fertig?   Fälligkeit    Priorität  Beschreibung
------------------------------------------------------------
Nein 23.10.1998 1 Kapitel 21 beenden
Nein 31.10.1998 1 Kapitel 20 beenden, überarbeiten
Nein 05.01.1999 2 Treffen mit Fred zum Essen
Ja 05.12.1998 3 Punkt 3
Nein 10.01.1999 5 Pyramide bauen

Um diese Daten ohne Formate auszugeben, würden Sie eine foreach-Schleife aufsetzen, die jeden Datensatz durchläuft, daraus eine Zeile aufbaut und diese dann an den Datei-Handle ausgibt. Da Sie keine Formate verwenden, müssen Sie selbst die Abstände zwischen den Daten kontrollieren, um sicherzustellen, dass die Daten korrekt ausgerichtet werden. Mit Formaten ist die ordentliche Ausrichtung der Daten wesentlich einfacher.

Um die Daten zu formatieren und auszugeben, überlegen wir zuerst, wie die Ausgabe mit der format-Deklaration aussehen soll. Genau genommen verwenden wir zwei format-Deklarationen: eine einfache Nur-Text-Deklaration für den Header und eine für die Datenzeilen. Außerdem deklarieren wir schon einmal unsere Formatvariablen, damit uns use strict keinen Ärger macht:

my ($done, $date, $prior, $desc); # Formatvariablen;

format STDOUT_TOP =
Fertig? Fälligkeit Priorität Beschreibung
------------------------------------------------------------
.
format STDOUT =
@|||| @<<<<<<<<< @| @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$done, $date, $prior, $desc
.

Die erste format-Deklaration (für STDOUT_TOP) ist eine besondere Art von Format, die einen Header für die Seite definiert. Header haben per definitionem den gleichen Namen wie das normale Format (hier STDOUT) plus dem angehängten _TOP. Sie könnten diesen Test als normale print-Anweisung ausgeben, aber formatierte Header haben den Vorteil, dass sie bei einem mehrseitigen Ausdruck auf jeder Seite automatisch gleich ausgegeben werden. Ich persönlich finde, dass sie das Formatieren stark vereinfachen, deshalb nehme ich sie meistens mit auf.

Kommen wir jetzt zu dem interessanteren, dem zweiten Format, das unsere eigentliche format-Deklaration enthält. Hier definieren wir die Breite und Ausrichtung all unserer Spalten: done und prior sind zentriert (beachten Sie das |-Zeichen), date und desc sind links ausgerichtet (<-Zeichen). Die Anzahl der Ausrichtungszeichen legt die Anzahl der Zeichen für die Spalte fest. Weisen die Daten mehr Zeichen auf, als die Spalte Zeichen aufnehmen kann, werden diese abgeschnitten.

Nachdem die Formate definiert sind, besteht der zweite Schritt darin, die Daten in einer Schleife zu durchlaufen und auszugeben. Doch zuerst sortieren wir die Datensätze nach ihrer Priorität:

my @sorteddata = sort { $a->{'prior'} <=> $b->{'prior'} } @data;

Anschließend durchlaufen wir die Daten und geben sie aus:

foreach (@sorteddata) {
my %rec = %$_;

if ($rec{'fertig'}) {
$done = 'Ja';
} else {
$done = 'Nein';
}
$date = $rec{'datum'};
$prior = $rec{'prior'};
$desc = $rec{'desc'};

write STDOUT;
}

Für jeden Teil jedes Datensatzes weisen Sie den temporären Variablen aus dem Format die auszugebenden Daten zu (die Variablen $done, $date, $prior und $desc). Die Daten für die Variable $done haben eigentlich die Werte 0 oder 1. In unserem Beispiel ersetzen wir diese Werte durch Ja oder Nein, um das Ergebnis anschaulicher zu machen.

Nachdem alle Variablen gesetzt wurden, besteht der letzte Schritt aus einer write- Anweisung, die das entsprechende Format verwendet, um eine Zeile von Daten auszugeben, und dann zum nächsten Datensatz in der Zeile weitergeht.

Weitere Informationen zu den Formaten finden Sie in der perlform-Manpage. Es lohnt sich auch, einen Blick auf die formatspezifischen Perl-Variablen in der perlvar- Manpage zu werfen, wo Format-Header, Seitenzahlen und anderes beschrieben sind. Schließlich gibt es noch das FileHandle-Modul, das Ihnen eine objektorientierte Schnittstelle zu den Datei-Handles und den Formaten bietet und die Arbeit mit vielen der in Perl definierten Elemente zur Verwaltung mehrerer Formate und Datei-Handles erleichtert.

Sockets

Netzwerke waren schon seit jeher eine Stärke von Unix, und Perl wäre schlecht beraten, wenn es nicht Zugriff auf die Netzwerk-Features von Unix bieten würde - insbesondere die TCP- und UDP-Sockets. Perl stellt eine Reihe von eigenen Funktionen für die Arbeit mit Sockets bereit (siehe Tabelle 20.1), die das gleiche Verhalten aufweisen wie ihre C-Gegenstücke. Wenn Sie in socketbasierter Programmierung bereits Erfahrung sammeln konnten, werden Ihnen diese Funktion vertraut vorkommen. Darüber hinaus können Sie das Socket-Modul nutzen, das Ihnen Zugriff auf die allgemeinen Socket-Strukturdefinitionen in C bietet.

Die Verwendung von Sockets in Perl hat jedoch auch seine Einschränkungen. Zum einen stehen Ihnen die meisten der Socket-Features von Perl nur unter Unix zur Verfügung. Unter Windows oder auf Macintosh-Rechner müssen Sie auf die entsprechenden Elemente von Win32 oder MacPerl zurückgreifen, wodurch Ihr Skript nicht mehr so portabel ist. Außerdem gilt es zu bedenken, dass die Chancen meist sehr gut stehen, dass die Aufgaben, die Sie mit Hilfe der Sockets erledigen wollen, bereits als Modul vorhanden sind - es sei denn, Sie wollen irgendein Netzwerkprotokoll implementieren, das nicht dem Standard entspricht. So lohnt es sich kaum, irgendwelche Teile für einen Webserver oder Webbrowser aufzusetzen, da es hinreichend Module gibt, die Ihnen diese Arbeit bereits abgenommen haben. Alles, was Sie tun müssen, ist, diese Module zu nutzen. Morgen werden wir uns hierzu ein Beispiel anschauen, das von den besonders nützlichen Modulen der LWP (Bibliothek für WWW-Zugriff in Perl) Gebrauch macht.

Suchen Sie im CPAN, vor allem im Abschnitt zu den Netzwerken, nach Modulen, die für einen Großteil der Netzwerkaufgaben, die Sie erledigen müssen, fertige Lösungen parat haben. Sollten Sie hier nicht fündig werden, schlagen Sie in der perlipc- Manpage nach, in der Sie weitere Informationen darüber finden, wie man Sockets verwendet (einschließlich einfacher Client- und Server-Beispiele).

In Tabelle 20.1 finden Sie die vordefinierten Funktionen für Sockets. Details zu diesen Funktionen befinden sich in der perlfunc-Manpage.

Funktion

Beschreibung

accept

Für Netzwerk-Server: Akzeptiert eine Socket-Verbindung von einem Client. Entspricht accept(2).

bind

Bindet eine Adresse an einen bereits geöffneten Socket-Datei-Handle. Entspricht bind(2).

connect

Für Netzwerk-Clients: Stellt eine Verbindung zu einem Server her, der auf eine Verbindung wartet. Entspricht connect(2).

getpeername

Liefert die Socket-Adresse vom anderen Ende einer Verbindung.

getsockname

Liefert die Socket-Adresse von diesem Ende einer Verbindung.

getsockopt

Liefert die Werte der Socket-Optionen.

listen

Für Server: Teilt dem System mit, auf Socket-Verbindungen zu diesem Socket zu lauschen und diese in eine Warteschleife zu stellen. Entspricht listen(2).

recv

Empfängt eine Nachricht an einen Socket. Entspricht recv(2).

send

Sendet eine Nachricht zu einem Socket. Entspricht send(2).

setsockopt

Setzt die Socket-Optionen.

shutdown

Schließt einen Socket mit einigen Steuerungsoptionen. Sie können auch close() verwenden.

socket

Öffnet einen Socket und verbindet ihn mit einem Datei-Handle.

socketpair

Öffnet ein Paar von unbenannten Sockets. Entspricht socketpair(2).

Tabelle 20.1: Socket-Funktionen

POD-Dateien

POD (ein Akronym für Plain Old Documentation, übersetzt mit »einfache, alte Dokumentation«) ist eine einfache Formatiersprache zum Erstellen von Dokumentationen zu Ihren Perl-Skripts oder -Modulen. In der Regel nehmen Sie diese Dokumentation direkt in Ihre Skriptdatei mit auf. Auf diese Art und Weise halten Sie das Skript und seine Dokumentation zusammen und müssen nicht beides getrennt warten. (Perl wird den POD-Inhalt einfach ignorieren, wenn Sie Ihre Skripts ausführen).

Um sich den POD-Inhalt Ihres Skripts (oder die POD-Dateien, die nur den POD-Inhalt enthalten) anzuschauen, können Sie das Skript perldoc verwenden (wird mit Perl ausgeliefert), mit dem sie den Text der Dokumentation auf Ihrem Bildschirm anzeigen lassen können. Sie können aber auch einen Übersetzer verwenden, der die POD- Dateien in ein anderes Format konvertiert - zum Beispiel pod2text für Textdateien, pod2html für HTML, pod2man für nroff-formatierte Manpages oder pod2latex für LaTeX-Formatierung. Bisher werden Sie wahrscheinlich perldoc verwendet haben, um die verschiedenen Teile der Perl-Dokumentation (im POD-Format) online einzusehen.

POD ist keine vollständige Textformatiersprache wie troff, LaTeX oder HTML. Wenn Sie also Ihre Skripts mit einer Unmenge an aufwendig formatierten Dokumentationen versehen wollen, sind Sie besser beraten, wenn Sie auf ein anderes Format zurückgreifen und Ihre Dokumentationsdateien getrennt von Ihren Skripts halten. POD-Dateien haben jedoch im allgemeinen den Vorteil, dass sie auf verschiedenen Plattformen und mit verschiedenen Systemen gelesen werden oder en passant in andere geläufigere Formate konvertiert werden können.

Beispiele für POD-Texte finden sich in fast jedem öffentlich verfügbaren Skript oder Modul, das mit Perl ausgeliefert wird oder aus dem CPAN heruntergeladen werden kann. Wenn Ihnen die nachfolgenden Ausführungen zu den POD-Dateien nicht genügen, finden Sie weitere Informationen in der perldoc-Manpage.

POD-Dateien erzeugen

POD-formatierter Text besteht aus Befehlsabsätzen und normalen Absätzen. Befehlsabsätze enthalten einfache Formatierungsanweisungen und etwas Text. Normale Absätze enthalten den eigentlichen Text. Sie können aber auch Befehle zur Zeichenformatierung in die normalen Absätzen mit aufnehmen.

Befehlsabsätze stehen in eigenen Zeilen und beginnen mit einem Gleichheitszeichen. Einige Befehlsabsätze weisen auch Text auf, der sich direkt an den Namen des Absatzes anschließt. Das Ende eines Befehlsabsatzes bildet eine leere Zeile.

Für Überschriften verwenden Sie die Befehlsabsätze =head1 und =head2, denen sich direkt der Text für die Überschrift anschließt. Diese Überschriften entsprechen in etwa den Tags <H1> und <H2> in HTML oder dem .SH-Tag in troff oder nroff.

Für Listen und andere eingerückte Elemente gibt es die Befehle =over, =item und =back. Mit =over beginnen Sie eine Liste, wobei eine optionale Zahl angibt, um wie viele Leerzeichen die Liste eingerückt werden soll. Jedes Listenelement beginnt mit einem =item-Tag und einem optionalen Zeichen, das das Symbol oder die Zahl vor diesem Element darstellt (Sie müssen sich selbst um die Numerierung kümmern, da diese nicht automatisch erfolgt). Am Ende der Liste heben Sie die mit =over eingeleitete Einrückung mit =back wieder auf.

Normale Absätze werden als einfache Absätze eingegeben, ohne sie mit irgendwelchen Befehlen zu kennzeichnen. Absätze, die ohne Whitespace-Zeichen beginnen, werden in der Regel so formatiert, dass sie die Seitenbreite füllen (wie <P> in HTML). Absätze mit einer Einrückung am Anfang werden unverändert übernommen (wie <PRE> in HTML). Alle Absätze müssen mit einer leeren Zeile enden.

In den Absätzen können Sie Zeichenformatierungscodes und Links mit aufnehmen, um ein bestimmtes Wort hervorzuheben oder einen Link einzufügen (der zu einer anderen Perl-bezogenen Manpage wie perlfunc oder ähnlichem führt). Im folgenden sehen Sie einige der geläufigeren Zeichenformate:

Um Text einzubetten, der in einer anderen Formatiersprache formatiert vorliegt - zum Beispiel HTML oder troff - gibt es ebenfalls Befehle: =for, =begin und =end. Text, der für bestimmte Formatiersprachen formatiert wurde, wird von dem speziellen Übersetzer unverarbeitet ausgegeben (der Übersetzer pod2html wird zum Beispiel formatiertes HTML direkt in die Ausgabe kopieren) und ansonsten einfach ignoriert. Verwenden Sie eingebettete Formatierungen, um in speziellen Fällen eine eingeschränkte Formatierung vorzunehmen.

POD in Skripts einbetten

Sie können POD-formatierten Text sowohl in einer eigenen Datei (in der Regel als Datei mit der Extension .pod) speichern oder ihn in Ihre Skript-Dateien integrieren, wo er schnell geändert und aktualisiert werden kann.

Um POD-Text in ein Skript aufzunehmen, markieren Sie den Anfang des POD- Textes mit einem beliebigen POD-Befehl (normalerweise =head1, obwohl auch =pod den Anfang eines POD-Textes anzeigen kann). Schließen Sie den POD-Text mit =cut ab. Auf diese Weise teilen Sie Perl mit, wann die Unterbrechung zu Ende ist und der Text weiter nach Code geparst werden kann.

POD-Text wird in der Regel am Anfang oder am Ende eines Skripts eingefügt. Sie können ihn aber auch irgendwo an einer beliebigen Stelle in Ihr Skript einfügen, zum Beispiel um das Verhalten einer Subroutine direkt über dem Code der Subroutine zu beschreiben. Solange Sie Ihren POD-Text mit einem Befehlsabsatz und =cut starten und enden, hat Perl keine Probleme damit.

Wenn Sie einen POD-Text an das Ende einer Datei setzen und eine __END__- Markierung verwenden (wie es zum Beispiel beim Erzeugen von Modulen vorkommt), müssen Sie darauf achten, dass eine Leerzeile nach dem __END__ kommt.

Code en passant ausführen

Ein weiteres nützliches Feature von Perl, das anderen Sprachen abgeschaut wurde, ist die Fähigkeit, einen String zur Laufzeit des Skripts als Perl-Code aufzufassen und auszuführen. Bewerkstelligt wird dies durch die Funktion eval, die einen String oder Block als Argument übernimmt und den darin enthaltenen Perl-Code kompiliert und ausführt, so als wenn er in einer Datei stehen oder als Perl-Einzeiler ausgeführt würde - und das alles innerhalb eines gerade laufenden Perl-Skripts.

Warum ist diese Funktion so nützlich? Aus einer Vielzahl von Gründen. Einer davon ist, dass Sie damit anspruchsvolle Strukturen und Funktionsaufrufe aufbauen können, ohne extensiv in if-else-Anweisungen verzweigen zu müssen: Stellen Sie den aufzurufenden Code aus aneinandergehängten Strings zusammen, und rufen Sie dann eval auf, um den String auszuführen. Es ist auch möglich, andere Dateien, die Perl- Code enthalten, von einem Perl-Skript aus einzulesen und auszuführen - ähnlich der Funktionsweise von use und require, jedoch immer zur Laufzeit Ihres Skripts (genau genommen ist require semantisch identisch mit eval, plus einigen Extraoptionen zum Suchen und Neuladen von Dateien). Des weiteren ist es möglich, Code bei Bedarf en passant auszuführen - der Perl-Debugger macht zum Beispiel in seiner Option zur Codeausführung davon Gebrauch. Oder Sie könnten mit Hilfe von eval einen Interpreter für Ihre eigene Sprache schreiben.

Mit Hilfe der eval-Funktion von Perl kann man Code vor der eigentlichen Ausführung testen und anschließende Fehler und ungewöhnliche Bedingungen, die aus der Ausführung des Codes resultieren, abfangen. Diese Möglichkeit wird in anderen Sprachen oft als Ausnahmenbehandlung bezeichnet. Wenn Sie zum Beispiel testen möchten, ob eine bestimmte Perl-Funktion - zum Beispiel fork - auf dem aktuellen System zur Verfügung steht, könnten Sie innerhalb von eval ein einfaches Beispiel ausprobieren, schauen, ob es funktioniert, und wenn ja, mit dem Skript und fork fortfahren. Scheitert eval, führen Sie einen alternativen Code aus. Die Ausnahmebehandlung mit eval ermöglicht eine robustere Fehlerprüfung und - behandlung in Ihren Skripts.

Eine Beschreibung von eval finden Sie in der perlfunc-Manpage.

Internationale Perl-Skripts erzeugen

Unter Internationalisierung, manchmal auch I18N genannt, versteht man die Verallgemeinerung von Skripts oder Programmen, so dass diese leicht in eine andere Sprache oder einen Dialekt übertragen oder übersetzt werden können. Von Lokalisierung, L10N, spricht man, wenn man eine internationalisierte Version eines Skripts nimmt und in einer speziellen Sprache zum Laufen bringt. Die Internationalisierung beginnt mit einfachen Maßnahmen - zum Beispiel alle Textstrings, die ein Skript verwendet, separat aufzubewahren, um diese ohne Änderungen am Perl-Code übersetzen zu können. Andere Dinge - die Arbeit mit anderen Zeichensätzen als dem Englischen ABC, die Verwendung anderer Sortier- und Vergleichsfunktionen für Texte, das Formatieren von Zahlen - können über die Verwendung des Lokale-Moduls von Perl gesteuert werden.

Weitere Informationen zur Anwendung und Verwaltung von Perl-Lokalen zur Erzeugung internationalisierter (oder lokalisierter) Skripts finden Sie in der perllocale- Manpage.

Skriptsicherheit mit Taint

Angenommen Sie schreiben ein Perl-Skript, das von Menschen ausgeführt wird, die Sie nicht kennen und denen Sie auch nicht besonders trauen - zum Beispiel wenn Sie eine Multi-User-Unix-Maschine verwalten oder wenn Ihr Skript für CGI verwendet wird. Da Sie denjenigen, der Ihr Skript ausführt, nicht kennen, könnte diese Person theoretisch feindliche Absichten hegen und Ihr Skript nutzen, um auf irgendeinem Weg autorisierten Zugriff auf Ihr System zu erhalten oder es in irgendeiner Weise zu mißbrauchen oder zu schädigen.

Wie können Sie verhindern, dass ein bösartiger Benutzer Ihr Skript mißbraucht oder Schaden anrichtet? Sorgfältige Programmierung ist eine Möglichkeit - zum Beispiel vorher prüfen, ob eine Eingabe irgendwelche heiklen Daten enthält, bevor sie einem system-Funktionsaufruf oder schrägen Anführungszeichen übergeben wird. Doch manchmal ist es schwierig, zu entscheiden, welche Daten unsicher sind, manchmal vergißt man, diese Prüfungen durchzuführen. In all diesen Fällen ist der Taint-Modus sehr hilfreich.

Der Taint-Modus wird in Perl mit der Option -T aktiviert. (Er wird außerdem automatisch ausgeführt, wenn sich die Benutzer- oder Gruppen-ID des Skripts von der Benutzer- oder Gruppen-ID der Person unterscheidet, die das Skript ausführt - zum Beispiel setuid-Skripts auf Unix-Systemen). Ist der Taint-Modus aktiviert, überwacht Perl die Daten, die in Ihr Skript gelangen - aus der Umgebung, als Befehlszeilenargumente oder als Daten von einem Datei-Handle (einschließlich der Standardeingabe). Wenn Sie versuchen, diese Daten dazu zu nutzen, irgend etwas außerhalb Ihres Skripts zu manipulieren, bricht Perl sofort ab. Um diese Daten trotzdem zu nutzen, müssen Sie Ihr Skript so schreiben, dass diese Daten teilweise geändert oder extrahiert werden - wodurch verhindert wird, dass unerwartete Daten unbemerkt durch das Skript hindurchrutschen.

Mit anderen Worten, der Taint-Modus ist kein eigener Sicherheitsmechanismus, aber er zwingt Sie, beim Schreiben Ihres Codes die Sicherheit immer im Auge zu behalten. Wenn Ihre Skripts zum Beispiel in einer unsicheren Umgebung ausgeführt werden, kann der Taint-Modus Ihnen helfen, sicherzustellen, dass Ihre Skripts nicht zu einladenden Sicherheitslecks in Ihrem System werden.

Weitere Informationen zum Taint-Modus und zur Sicherheit in Perl finden Sie in der perlsec-Manpage. Wenn Sie allgemeinere Auskünfte zum Thema Sicherheit und CGI benötigen, schauen Sie doch mal in den häufig gestellten Fragen (FAQs) zur Sicherheit im WWW unter http://www-genome.wi.mit.edu/WWW/faqs/www-security-faq.html nach.

Wenn Ihnen speziell die Sicherheit Ihrer Skripts am Herzen liegt, sollten Sie Penguin aus dem CPAN ausprobieren. Penguin stellt eine Umgebung bereit, mit der es möglich ist, Codefragmente zu verschlüsseln und digital zu signieren, bevor sie an andere Sites verschickt werden. Am Zielort entschlüsselt Penguin diese Daten wieder und prüft die Zuverlässigkeit dieses Codes vor seiner Ausführung. Und selbst dann führt Penguin den Code noch innerhalb streng kontrollierter Grenzen aus. Betrachten Sie Penguin einfach als ein Gegenstück zu Javas Mechanismus zum Signieren von Applets und der anschließenden Ausführung innerhalb eines geschlossenen, sicheren »Sandkastens«. Im CPAN finden Sie ausführliche Informationen über Penguin.

PerlScript

PerlScript - Teil der ActiveState-Version von Perl für Windows - ist eine Skripting- Maschine für ActiveX-Komponenten. Mit PerlScript können Sie Perl als Skript- Sprache in jeden ActiveX-Skripting-Host verwenden - zum Beispiel im Internet Explorer, IIS oder in einem beliebigen Webserver oder sogar im Windows Scripting Host (WHS) von Microsoft selbst.

Microsofts Skripting-Maschinen für ActiveX-Komponenten unterstützen per Vorgabe VBScript- und JavaScript-Skripting-Maschinen. Diese Sprachen sind zwar für die meisten Zwecke vollkommen ausreichend, doch kann Ihnen Perl in vielen Fällen weitere und leistungsstärkere Optionen bieten. Außerdem ist es für Programmierer, die bereits mit Perl vertraut sind, ein unschätzbarer Vorteil, wenn Sie einfach mit Perl weiterarbeiten können, anstatt zu anderen Sprachen wechseln zu müssen.

Sie können mit PerlScript auch für das Web programmieren und Perl-Skripts in Webseiten einbetten, und das sowohl clientseitig (Webbrowser) als auch serverseitig (Webserver) - so wie man ansonsten JavaScript- und VBScript-Skripts oder ASP- Seiten (Active Server Pages) nutzt.

PerlScript läßt sich auch mit dem Windows Scripting Host verwenden. Dadurch haben Sie die Möglichkeit, verschiedene Aspekte des Windows-Systems über Skripts zu steuern (dient als Ersatz für die alten DOS-Batch-Dateien). Zur Zeit gibt es den Windows Scripting Host standardmäßig nur zusammen mit Windows 98. Er läßt sich aber für Windows 95 und Windows NT von msdn.microsoft.com/scripting herunterladen und installieren.

PerlScript wird automatisch mit der ActiveState-Version von Perl für Windows installiert. Sie können es aber auch als eigene Komponente von der ActiveState- Webseite herunterladen (Sie müssen allerdings eine Version von Perl für Windows installiert haben).

Weitere allgemeine Informationen zu ActiveX-Skripting finden Sie unter http:// msdn.microsoft.com/scripting. Details zu PerlScript lassen sich in der Dokumentation zu PerlScript unter http://www.activestate.com/activeperl/docs/ perlscript.html oder in dem ausgezeichneten »Complete Guide to PerlScript« (vollständiger PerlScript-Führer) von Matt Sargeant unter http:// www.fastnetltd.ndirect.co.uk/Perl/Articles/PSIntro.html nachlesen.

Perl-Erweiterungen

Eine Perl-Erweiterung ist ein Weg, eine externe Bibliothek, in der Regel in C geschrieben, in Perl-Skripts einzubinden. Wenn Sie eine Perl-Erweiterung erzeugen, erzeugen Sie sozusagen einen speziellen Code, der es Ihnen ermöglicht, diese externe Bibliothek wie ein beliebiges Perl-Modul mit use in Ihre Perl-Skripts zu importieren. Wenn Sie genau hinschauen, werden Sie feststellen, dass viele der Perl-Module im CPAN sowohl Perl-Code als auch Perl-Erweiterungen verwenden.

Für die Erzeugung einer Perl-Erweiterung müssen Sie eine Sprache namens XS verwenden, die den »Kleber« zwischen Perl und C bereitstellt (Perl-Erweiterungen werden in Anlehnung an die XSUB-Funktion in der XS-Sprache manchmal auch XSUBs genannt). Sie können XS-Dateien selbst schreiben oder sie aus einer bestehenden C-Bibliothek erzeugen. Außerdem gibt es besondere Make-Dateien, die erstellt oder erzeugt werden müssen, damit am Ende alles zusammenpaßt (eine Make- Datei ist ein Skript, das verwendet wird, um ein Projekt zu kompilieren und die Abhängigkeiten zwischen mehreren Dateien eines Projekts zu verwalten).

In der Perl-Distribution findet sich eine Reihe von Tools und Modulen, die das Entwikkeln von Perl-Erweiterungen vereinfachen - einschließlich Tools, um durch Erzeugung von XS-Dateien bestehende C-Bibliotheken in Perl einzubinden oder um C-Code aus XS-Dateien zu erzeugen. Besonders erwähnenswert sind in diesem Zusammenhang die Module des ExtUtils-Pakets, besonders ExtUtils::MakeMaker (die Ihnen helfen, Make-Dateien zu erzeugen).

Bevor Sie damit beginnen, Perl-Erweiterungen zu erzeugen, sollten Sie über ausreichend Hintergrundwissen zu der Entwicklung von Perl-Modulen verfügen. Dieses Hintergrundwissen können Sie sich in der perlmod-Manpage aneignen. Eine Einführung in die Erzeugung von Erweiterungen finden Sie in der perlxstut-Manpage. Des weiteren sollten Sie einen Blick in perlxs (das XS-Referenzhandbuch), h2xs (ein Skript zum Konvertieren von C-Headerdateien in XS-Dateien) und perguts (für interne Perl-Funktionen) werfen. Die POD-Dateien in den verschiedenen ExtUtils- Modulen enthalten Erläuterungen, wie diese Module anzuwenden sind.

Neue und fortgeschrittene Elemente in Perl 5.005

Perl 5.005 wurde während der Arbeit an diesem Buch herausgebracht. Ich habe mich bemüht, auf alle Unterschiede und neuen Teile von Perl hinzuweisen, die für Ihre Arbeit mit der neuen Version von Bedeutung sein könnten. Für den Großteil von Perl sind allerdings nur geringfügige Unterschiede zwischen der Perl-Version 5.005 und der früheren Version 5 zu verzeichnen. Radikale Änderungen sollten Ihnen eigentlich nicht auffallen. Wenn Sie aber bereits über Perl 5.005 verfügen und gern etwas herumspielen wollen, finden Sie in dieser Version eine Reihe von neuen Optionen. Einige davon, die bisher im Buch noch keine Erwähnung fanden, möchte ich Ihnen kurz vorstellen.

Die wahrscheinlich bedeutendste Änderung in Perl 5.005 ist der Einsatz von Threading. Rufen Sie sich dazu noch einmal Kapitel 18, »Perl und das Betriebssystem«, in Erinnerung. Dort habe ich die fork-Funktion und den Einsatz von Prozessen behandelt und allgemein konstatiert, dass die Prozeßfähigkeiten der Unix- Version von Perl sich nur schwer auf andere Plattformen übertragen lassen. Threading soll dieses Problem jetzt lösen. Sie können damit mehrere gleichzeitig laufende Threads erzeugen, die unabhängig voneinander gestartet und gestoppt werden können. Wichtiger noch ist jedoch die Tatsache, dass Threading in der Perl- Sprache plattformübergreifend unterstützt wird, was bedeutet, dass ein für Unix geschriebenes komplexes Skript mit mehreren Threads sich genausogut unter Windows oder auf einem Macintosh-Rechner ausführen läßt. Perls Thread-System ist in dem Thread-Modul definiert, das man einbinden muss, um mit Threads arbeiten zu können. Beachten Sie, dass sich die Thread-Unterstützung noch im Beta-Stadium befindet - mit anderen Worten, sie kann Fehler aufweisen und Schwierigkeiten in der Anwendung bereiten. Außerdem muss man davon ausgehen, dass die Thread- Unterstützung in zukünftigen Versionen von Perl noch etliche Änderungen erfahren wird. Informieren Sie sich auf alle Fälle in der POD-Dokumentation, die bei dem Thread-Modul dabei ist.

Eine zweite bedeutsame Änderung ist die Integration eines Perl-Compilers. Der Compiler, mit Namen perlcc, kann ein Perl-Modul in eine native Bibliothek konvertieren/kompilieren oder ein Perl-Skript in C-Code konvertieren und diesen Code in eine ausführbare Datei kompilieren. Genau genommen ist perlcc das Front- End zu einer generischen Kompilierendstufe in Perl (die Module B und O), mit der Sie Perl-Skripts in quasi alles konvertieren oder kompilieren können.

Perl 5.005 enthält außerdem eine neue Maschine für reguläre Ausdrücke, die selbsttätig etliche Fehler behebt und einige Neuheiten aufweist. Um die Performance zu verbessern, können Sie reguläre Ausdrücke außerdem mit dem neuen C-Operator vorkompilieren. Weitere Informationen hierzu finden Sie in den Manpages perlre und perlop.

Vertiefung

Da dieses ganze Kapitel ein einzig großer Vertiefungsabschnitt ist, gibt es eigentlich nicht allzuviel in diesem Abschnitt zu sagen. Deshalb möchte ich Sie an dieser Stelle noch einmal auf folgendes aufmerksam machen: Wenn Sie Probleme, Fragen oder Schwierigkeiten haben oder einfach nur wissen wollen, wie sich ein bestimmter Perl- Teil verhält - von den elementaren Operatoren über die regulären Ausdrücken und die Referenzen bis zu den Modulen -, sollten Sie neben der Perl-Dokumentation (Manpages und POD-Dateien) auf alle Fälle die häufig gestellten Fragen (FAQs) zu Perl zu Rate ziehen. Das Kamelbuch (Programmieren mit Perl, das ich am Tag 1 im Vertiefungsabschnitt erwähnt habe) kann Ihnen helfen zu verstehen, wie sich Perl in verschiedenen Situationen verhält. Und wenn Sie trotzdem immer noch festhängen, nutzen Sie das Web - www.perl.com, www.activestate.com (für Windows) und viele der anderen Websites, die ich im Laufe dieses Buches erwähnt habe, können Ihnen dabei behilflich sein. Schließlich gibt es noch eine Reihe von Newsgroups (wie die comp.perl-Hierarchie - insbesondere comp.perl.misc) und Mailing-Listen, über die Sie andere Perl-Programmierer kontaktieren können.

Viel Glück!

Zusammenfassung

Heute ist der Tag, an dem wir alle losen Enden zusammenbinden. So haben wir heute eine Reihe von zusätzlichen Perl-Elementen betrachtet, die ich aus Zeit- und Platzgründen in keinem der anderen 19 Kapiteln unterbringen konnte. Dieses Mischmasch umfaßt:

Meinen Glückwunsch! Jetzt bleibt uns nur noch ein Tag, und der ist ganz den Beispielen gewidmet. Sie haben Perl in ausreichendem Umfang kennengelernt, um einige aufregende Dinge zu machen. Also nur zu! Und vergessen Sie nicht - Perl ist ein Gemeinschaftsprodukt. Nutzen Sie die Module im CPAN. Und sollten Sie selbst etwas schreiben, von dem Sie meinen, dass auch andere es gebrauchen könnten, stellen Sie es doch auch in das CPAN.

Fragen und Antworten

Frage:
Meine Einzeiler funktionieren nicht in MacPerl.

Antwort:
Stellen Sie im Dialog Edit->Preferences->Scripts sicher, dass Sie die Option Run Scripts Opened from Finder markiert haben. Wenn Sie hier Edit ausgewählt haben, lassen sich Ihre Einzeiler nicht ausführen.

Frage:
Meine Einzeiler funktionieren nicht unter Windows.

Antwort:
Es gibt Unterschiede zwischen den Anführungszeichen in der DOS/Windows- Befehlszeile und in Unix. Unter Unix können Sie einfache und doppelte Anführungszeichen verwenden, unter Windows nur doppelte. Deshalb sollten Sie sich vergewissern, dass Ihre Einzeiler unter Windows vorn und hinten in doppelten Anführungszeichen stehen und alle Anführungszeichen im Skript selbst mit einem Backslash markiert sind.

Frage:
Meine Einzeiler funktionieren nicht in Unix.

Antwort:
Sie funktionieren nicht? Haben Sie das Skript auch wirklich in eine Zeile eingegeben, ohne Return-Zeichen? Haben Sie auch nicht vergessen, das gesamte Skript in einfache Anführungszeichen zu setzen? Haben Sie an die Perl-Option -e gedacht? Wenn Sie alle diese Punkte überprüft haben, stellen Sie sicher, dass Sie das gleiche Skript ausführen können, wenn Sie es in einer Datei abspeichern.

Frage:
Aus Ihrer Beschreibung entnehme ich, dass Perl die OOP-Konzepte der öffentlichen (public) und privaten (private) Daten nicht unterstützt. Was sollte dann jemand daran hindern, Methoden zu verwenden, die nicht als Teil der öffentlichen API ihrer Klasse gedacht sind?

Antwort:
Nichts. Das objektorientierte Modell von Perl unterscheidet nicht zwischen privater und öffentlicher API. Es geht davon aus, dass Sie und die Programmierer, die Ihre Klassen verwenden, sich anständig verhalten, was die API angeht. Als Entwickler einer Klasse sollten Sie Ihre API dokumentieren und angeben, welche Ihrer Methoden öffentlich sind (dazu eignet sich POD sehr gut), und dann davon ausgehen, dass alle, die Ihre Klasse verwenden, sich auch an diese Vorgaben halten. Umgekehrt sollten auch Sie, wenn Sie die Klassen anderer Programmierer verwenden, sich an deren dokumentierte öffentliche API halten und interne Methoden oder Daten nur verwenden, wenn Sie genau wissen, was Sie tun (und das Risiko zu tragen bereit sind).

Frage:
Mehrfachvererbung ist sehr verwirrend und fehleranfällig. Warum hat sich Perl nicht auf die einfache Vererbung beschränkt?

Antwort:
Um damit die Mächtigkeit der Sprache zu beschränken? Es sollte Ihnen an dieser Stelle im Buch bereits aufgefallen sein, dass Perl zu keiner Zeit wirklich versucht, den Programmierer in seiner Arbeit Grenzen zu setzen. Mehrfachvererbung mag zwar oft verwirrend und auch schwierig zu debuggen sein, aber sie ist auch unglaublich leistungsstark, wenn Sie zusammen mit einem guten Design verwendet wird (und vergessen Sie nicht, ein gutes Design bedingt nicht unbedingt Vererbung). Die Entwickler von Perl lassen Ihnen lieber genug Seil, um sich aufzuhängen, als dass sie Sie zu knapp halten, so dass Sie letztlich nicht genug Seil haben, um einen Knoten zu binden.

Frage:
Wo, zum Teufel, kommen die Begriffe I18N und L10N her?

Antwort:
Es stehen jeweils 18 Buchstaben zwischen dem I und dem N in dem Wort »Internationalization« (Internationalisierung) und 10 Buchstaben zwischen dem L und dem N in »Localization« (Lokalisierung). Internationalization und Localization sind wirklich sehr lange Wörter. Programmierer mögen das nicht.

Frage:
Ihre Beschreibung der Perl-Extensionen erläutert, wie man C-Code aus einem Perl-Skript heraus aufruft. Wie ruft man Perl-Code von einem C-Programm aus auf?

Antwort:
Schlagen Sie in der perlcall-Manpage nach. Außerdem müssen Sie sich in der XS-Sprache auskennen und wissen, wie man Erweiterungen konstruiert.

Workshop

Der Workshop enthält Quizfragen, die Ihnen helfen sollen, Ihr Wissen zu festigen, und Übungen, die Sie anregen sollen, das eben Gelernte umzusetzen und eigene Erfahrungen zu sammeln. Versuchen Sie, das Quiz und die Übungen zu beantworten und zu verstehen, bevor Sie zur Lektion des nächsten Tages übergehen.

Quiz

  1. Was bewirken die folgenden Perl-Schalter (zusammen mit Einzeilern)?
        -e
      -i
      -p
  2. Wie werden in Perl Klassen, Methoden und Instanzvariablen realisiert?
  3. Zeigen Sie zwei Wege auf, wie man eine Methode aufrufen kann.
  4. Wie wird nach mehrfachvererbten Superklassen gesucht, wenn eine Methode aufgerufen wird?
  5. Was sind selbstladende Methoden?
  6. Wozu werden Perl-Formate verwendet? Wie lauten die zwei Funktionen, die zur Verwendung von Formaten benötigt werden?
  7. Was ist eine POD-Datei? Warum sollten Sie in Ihren Perl-Skripts POD anderen Formaten wie HTML vorziehen?
  8. Wozu dient die Funktion eval? Wozu verwendet man sie?
  9. Wozu wird der Taint-Modus verwendet? Wie aktivieren Sie ihn?
  10. Was ist ein XSUB?
  11. Perl 5.005 erweitert Perl um die Fähigkeit zum Multithreading. Wozu verwendet man Threads, und warum sind sie so nützlich für die Perl-Programmierer?

Übungen

  1. Schreiben Sie einen Perl-Einzeiler, der alle Vorkommen des Buchstaben »t« in der Eingabe zählt und das Ergebnis ausgibt.
  2. Schreiben Sie einen Perl-Einzeiler, der die Summe der Eingabe berechnet (unter der Annahme, dass die Eingabe numerisch ist).
  3. Schreiben Sie einen Perl-Einzeiler, der alle Vorkommen von drei aufeinanderfolgenden Leerzeichen durch einem Tabulator ersetzt und das Ergebnis in einer Datei gleichen Namens speichert.
  4. FEHLERSUCHE: Was ist an diesem Einzeiler falsch?
        perl -p 's/t/T/q';
  5. FEHLERSUCHE: Und was ist mit diesem? (HINWEIS: Es gibt mehr als ein Problem)
        perl -ne 'print 'Zeile: ', reverse $_;';
  6. Schreiben Sie eine Perl-Klasse namens EinfacheKlasse, die drei Instanzvariablen enthält: a, b und c. Nehmen Sie folgende Methoden mit auf:

Antworten

Hier die Antworten auf die Workshop-Fragen aus dem vorigen Abschnitt.

Antworten zum Quiz

  1. Die Option -e führt ein Perl-Skript (einen Einzeiler) von der Befehlszeile aus.
  2. Die Option -i bearbeitet Dateien. Das bedeutet, das Ergebnis des Skripts wird in der Originaldatei gespeichert. Wird der Option -i eine Dateiextension als Argument mitgegeben, wird diese verwendet, um eine Sicherungsdatei der Originalversion der Datei zu erstellen.
  3. Die Option -p bettet den Perl-Einzeiler in eine while-Schleife (<>) mit einer print- Anweisung am Ende ein. Wenn Sie die Zeilen einer Datei einzeln bearbeiten und dann ausgeben wollen, sollten Sie diese Option verwenden, um sich Tipparbeit zu ersparen.
  4. Klassen sind in Perl Pakete, Methoden sind Subroutinen, die entweder einen Klassennamen oder eine Objektreferenz als erstes Argument haben. Instanzvariablen werden in der Regel als Hash-Elemente implementiert, wobei die Namen der Variablen als Schlüssel dienen.
  5. Sie können Methoden wie reguläre Funktionen aufrufen:
        methode $obj,1,2,3;
  6. oder die Dereferenzierungssyntax verwenden:
        $obj->methode(1,2,3);
  7. Mehrfachvererbte Superklassen werden vertikal durchsucht. Das heißt, zuerst werden die erste Superklasse in der Liste und alle ihre Superklassen durchsucht, bevor die nächste Superklasse in der Liste durchsucht wird.
  8. Selbstladende Methoden (AUTOLOAD) werden aufgerufen, wenn in einem Skript eine Methode aufgerufen wird, für die es weder in der aktuellen Klasse noch in einer ihrer Superklassen eine zugehörige Methodendefinition gibt. Sie können selbstladende Methoden dazu nutzen, um Methoden (wie zum Beispiel die Lese- und Schreibmethoden für Variablen) zusammenzufassen oder um Aufrufe unbekannter Methodennamen aufzufangen.
  9. Perl-Formate werden verwendet, um Daten tabellarisch für die Textausgabe aufzubereiten. Die Daten werden automatisch der eingestellten Breite angepaßt, wobei die einzelnen Spalten nach den von Ihnen definierten Formatvorgaben ausgerichtet oder ausgefüllt werden. Sie erzeugen ein Format, indem Sie mit format eine Formatschablone definieren und das Ergebnis mit write ausgeben.
  10. POD steht für Plain Old Documentation. Mit POD können Sie auf einfache Weise online-zugängliche Dokumentationen zu Ihren Perl-Skripts erzeugen. POD ist ein allgemein nützliches Dokumentationsformat, das leicht extrahiert und in andere Ausgabeformate wie HTML oder troff konvertiert werden kann.
  11. Die Funktion eval wird verwendet, um Perl-Codefragmente en passant während der Ausführung eines Skripts auszuwerten. Sie können eval dazu nutzen, um andere Skripts in Ihr aktuelles Skript aufzunehmen oder Codeabschnitte zu testen, bevor Sie sie richtig ausführen lassen.
  12. Der Taint-Modus von Perl dient dazu, Ihre Perl-Skripts gegen Fehler und Versäumnisse zu schützen, die zu unsicherem Code führen können. Unsicherer Code, der in einer unsicheren Umgebung ausgeführt wird, kann böswilligen Benutzern die Gelegenheit bieten, Ihr System zu manipulieren oder sogar zu schädigen. Der Taint-Modus legt alle externen Daten und Einstellungen in einer besonderen, kontrollierten Umgebung ab und verhindert so, dass Sie diese Daten zufällig zu Ihrem eigenen Schaden nutzen. Sie aktivieren den Taint-Modus mit der Perl-Option -T. Der Modus wird allerdings auch automatisch eingeschaltet, wenn sich die Benutzer- oder Gruppen-ID des Skripts von der Benutzer- oder Gruppen- ID der Person unterscheidet, die das Skript ausführt (ist beispielsweise für CGI- Skripts, die auf einem Webserver ausgeführt werden, der Fall).
  13. XSUB ist der umgangssprachliche Name für eine Perl-Erweiterung: ein natives Codefragment, das von einem Perl-Skript aus ausgeführt werden soll. Die Bezeichnung XSUB leitet sich von einer Funktion in der XS-Sprache ab.
  14. Multithreading ermöglicht die gleichzeitige Ausführung mehrerer »Threads« (zu Deutsch »Fäden«) in einem Perl-Skript. Perl-Threads sind nicht nur deshalb so interessant, weil Sie den Perl-Programmierern neue Möglichkeiten eröffnen und höhere Flexibilität bieten, sondern weil sie plattformübergreifend verwendet werden können. Vorher war diese Art von Verhalten nur durch die Unix-Funktion fork zu bewerkstelligen. Aber mit der Verwendung von fork waren Ihre Skripts nicht mehr auf andere Plattformen portierbar. Durch Threads wurde das Problem jetzt gelöst.

Lösungen zu den Übungen

  1. Eine mögliche Lösung ist:
        perl -e ' while (<>){while (/t/g){$c++;}};print $c;' datei.txt
  2. Eine mögliche Lösung lautet:
        perl -e 'while(<>){$sum+=$_;}print "$sum\n";'
  3. Eine mögliche Lösung lautet:
        perl -pe -i.bak 's/   /\t/g' ozy.txt
  4. Die Option -e fehlt.
  5. Dieses Beispiel weist zwei Probleme auf: eines syntaktischer und eines konzeptioneller Art. Der erste Fehler ist, dass man einfache Anführungszeichen nicht ineinander verschachteln kann. Ersetzen Sie die inneren einfachen Anführungszeichen durch doppelte Anführungszeichen.
  6. Das zweite Problem betrifft die Funktion reverse. Dieser Einzeiler erweckt den Eindruck, dass die einzelnen Eingabezeilen zeichenweise in umgekehrter Reihenfolge mit dem Wort »Zeile« am Anfang ausgegeben werden. Sie sollten sich jedoch in Erinnerung rufen, dass die reverse-Funktion je nach Kontext (Skalar- oder Listenkontext) ein unterschiedliches Verhalten aufweist. In diesem Falle wird reverse in einem Listenkontext aufgerufen, da die Argumente zu print immer eine Liste oder eine Kombination von Listen sind. Doch damit wird nur die Reihenfolge der Zeilen in einem Array umgedreht. Das Argument $_ wird dann in eine Liste konvertiert, diese Liste mit einer Zeile umgedreht und anschließend ausgegeben. Mit anderen Worten, äußerlich passiert nichts.
  7. Damit der String wirklich umgedreht wird, müssen Sie die reverse-Funktion in einem skalaren Kontext aufrufen. Dieser Fehler kann schnell mit der scalar- Funktion behoben werden:
        perl -ne 'print 'Zeile: ', scalar (reverse $_);'
  8. Achten Sie auf die Neue-Zeile-Zeichen. Wenn Sie eine Zeile mit einem Neue- Zeile-Zeichen am Ende umdrehen, wird dieses Zeichen an den Anfang gesetzt. Die bessere Lösung ist, den String von dem Neue-Zeile-Zeichen zu befreien, ihn mit reverse umzudrehen und ihn dann mit dem frisch angehängten Neue-Zeile- Zeichen wieder auszugeben:
        perl -ne 'chomp;print "Zeile: ", scalar(reverse $_), "\n" ; '
  9. Zu unserer Bequemlichkeit gibt es eine Perl-Option, die diese Schritte vereinfacht: die Option -l. Damit wird das Neue-Zeile-Zeichen von der Eingabe entfernt, um es (oder irgendein anderes Zeilenende-Zeichen Ihrer Wahl) hinterher für Sie wieder anzuhängen:
        perl -lne 'print "Zeile: ", scalar(reverse $_);'
  10. Hier eine mögliche Lösung (mit Code, um das Ergebnis am Ende zu testen). Die folgende Version zeigt drei Wege auf, wie man auf Instanzvariablen zugreifen kann. Beachten Sie, dass keine dieser Methoden besonders robust ist. Keine von ihnen prüft, ob Sie versuchen, die Werte von nichtexistenten Variablen zu lesen oder zu setzen (und in der Tat werden die generischen Versionen mit Freude eine Instanzvariable hinzufügen, die nicht a, b oder c heißt).
        #!/usr/bin/perl -w

    package EinfacheKlasse;

    sub new {
    my ($classname, $a, $b, $c) = @_;
    my $self = {};
    $self->{a} = $a;
    $self->{b} = $b;
    $self->{c} = $c;
    return bless $self, $classname;
    }

    # der längste Weg
    sub getA {
    my $self = shift;
    return $self->{a};
    }
    sub setA {
    my $self = shift;
    if (@_) {
    $self->{a} = shift;
    } else {
    warn "kein Argument; verwende undef\n";
    $self->{a} = undef;
    }
    }

    # ein etwas allgemeinerer Weg, der mehr Argumente benötigt.
    sub get {
    my ($self, $var) = @_;
    if (!defined $var) {
    print "Keine Variable!\n";
    return undef;
    } elsif (!defined $self->{$var}) {
    print "Variable nicht definiert oder ohne Wert.\n";
    return undef;
    } else {
    return $self->{$var};
    }
    }
    sub set {
    my ($self, $var, $val) = @_;
    if (!defined $var or !defined $val) {
    print "Brauche Variable und Wert als Argument!";
    return undef;
    } else {
    $self->{$var} = $val;
    return $val;
    }
    }


    # ein wirklich allgemeiner Weg
    sub AUTOLOAD {
    my $self = shift;
    my $var = $AUTOLOAD;
    $var =~ s/.*:://;
    $var =~ s/[gs]et//;
    $var = lc $var;

    if (@_) {
    $self->{$var} = shift;
    return $self->{$var};
    } else {
    return $self->{$var};
    }
    }

    sub sum {
    my $self = shift;
    my $sum = 0;
    foreach ('a','b','c') {
    if (!defined $self->{$_} or $self->{$_} !~ /^\d+/ ) {
    warn "Variable $_ enthaelt keine Zahl.\n";
    } else { $sum += $self->{$_}; }
    }
    return $sum;
    }

    package main;
    $obj = new EinfacheKlasse (10,20,30);

    print "A: ", $obj->getA(), "\n";
    $obj->setA("foo");
    print "A: ", $obj->getA(), "\n";

    print "B: ", $obj->get('b'), "\n";
    $obj->set('b', 'bar');
    print "B: ", $obj->get('b'), "\n";

    # es gibt keine getC-Methode; autoload
    print "C: ", $obj->getC(), "\n";
    $obj->setC('baz'); # ditto setC
    print "C: ", $obj->getC(), "\n";

    # zurücksetzen
    print "\nA: 10\n";
    $obj = new EinfacheKlasse (10);
    print "Summe: ", $obj->sum(), "\n";
    print "\nA: 10 B: 5\n";
    $obj = new EinfacheKlasse (10,5);
    print "Summe: ", $obj->sum(), "\n";
    print "\nA: 10 B: 5 C: 5\n";
    $obj = new EinfacheKlasse (10,5,5);
    print "Summe: ", $obj->sum(), "\n";


vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbacknächstes Kapitel


© Markt&Technik Verlag, ein Imprint der Pearson Education Deutschland GmbH